﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Granite.Metadata;
using Granite.DataAnnotations;
using System.Collections;

namespace Granite.Commons.Tests.Metadata
{

    [TestClass]
    public class MetadataCacheTests
    {
        [TestMethod]
        public void MetadataCache_Test1()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            Assert.IsNotNull(result, "GetMetadata returned a null");

            var publicProperty = result.Properties["PublicProperty"];
            Assert.IsNotNull(publicProperty);
            Assert.IsFalse(publicProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, publicProperty.CalculatedFields.Count);
            Assert.IsTrue(publicProperty.CanRead);
            Assert.IsTrue(publicProperty.CanWrite);
            Assert.AreEqual("PublicProperty", publicProperty.Name);
            Assert.AreEqual("PublicProperty", publicProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), publicProperty.PropertyType);
            Assert.AreEqual(0, publicProperty.Validators.Count);

            var publicPrivateProperty = result.Properties["PublicPrivateProperty"];
            Assert.IsNotNull(publicPrivateProperty);
            Assert.IsFalse(publicPrivateProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, publicPrivateProperty.CalculatedFields.Count);
            Assert.IsTrue(publicPrivateProperty.CanRead);
            Assert.IsFalse(publicPrivateProperty.CanWrite);
            Assert.AreEqual("PublicPrivateProperty", publicPrivateProperty.Name);
            Assert.AreEqual("PublicPrivateProperty", publicPrivateProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), publicPrivateProperty.PropertyType);
            Assert.AreEqual(0, publicPrivateProperty.Validators.Count);

            var publicProtectedProperty = result.Properties["PublicProtectedProperty"];
            Assert.IsNotNull(publicProtectedProperty);
            Assert.IsFalse(publicProtectedProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, publicProtectedProperty.CalculatedFields.Count);
            Assert.IsTrue(publicProtectedProperty.CanRead);
            Assert.IsFalse(publicProtectedProperty.CanWrite);
            Assert.AreEqual("PublicProtectedProperty", publicProtectedProperty.Name);
            Assert.AreEqual("PublicProtectedProperty", publicProtectedProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), publicProtectedProperty.PropertyType);
            Assert.AreEqual(0, publicProtectedProperty.Validators.Count);

            var protectedProperty = result.Properties["ProtectedProperty"];
            Assert.IsNotNull(protectedProperty);
            Assert.IsFalse(protectedProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, protectedProperty.CalculatedFields.Count);
            Assert.IsFalse(protectedProperty.CanRead);
            Assert.IsFalse(protectedProperty.CanWrite);
            Assert.AreEqual("ProtectedProperty", protectedProperty.Name);
            Assert.AreEqual("ProtectedProperty", protectedProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), protectedProperty.PropertyType);
            Assert.AreEqual(0, protectedProperty.Validators.Count);

            var privateProperty = result.Properties["PrivateProperty"];
            Assert.IsNotNull(privateProperty);
            Assert.IsFalse(privateProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, privateProperty.CalculatedFields.Count);
            Assert.IsFalse(privateProperty.CanRead);
            Assert.IsFalse(privateProperty.CanWrite);
            Assert.AreEqual("PrivateProperty", privateProperty.Name);
            Assert.AreEqual("PrivateProperty", privateProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), privateProperty.PropertyType);
            Assert.AreEqual(0, privateProperty.Validators.Count);

            var setOnlyProperty = result.Properties["SetOnlyProperty"];
            Assert.IsNotNull(setOnlyProperty);
            Assert.IsFalse(setOnlyProperty.AffectsCalculatedFields);
            Assert.AreEqual(0, setOnlyProperty.CalculatedFields.Count);
            Assert.IsFalse(setOnlyProperty.CanRead);
            Assert.IsTrue(setOnlyProperty.CanWrite);
            Assert.AreEqual("SetOnlyProperty", setOnlyProperty.Name);
            Assert.AreEqual("SetOnlyProperty", setOnlyProperty.PropertyChangedEventArgs.PropertyName);
            Assert.AreEqual(typeof(int), setOnlyProperty.PropertyType);
            Assert.AreEqual(0, setOnlyProperty.Validators.Count);
        }

        [TestMethod]
        public void MetadataCache_Test2()
        {
            try
            {
                MetadataCache.GetMetadata(null);
                Assert.Fail("Expected ArgumentNullException");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("type", ex.ParamName);
            }
        }

        //[TestMethod]
        //public void MetadataCache_Test6()
        //{
        //    try
        //    {
        //        new PropertyMetadata(null);
        //        Assert.Fail("Expected ArgumentNullException");
        //    }
        //    catch (ArgumentNullException ex)
        //    {
        //        Assert.AreEqual("info", ex.ParamName);
        //    }
        //}


        //[TestMethod]
        //public void MetadataCache_Test3()
        //{
        //    try
        //    {
        //        new ClassMetadata(null);
        //        Assert.Fail("Expected ArgumentNullException");
        //    }
        //    catch (ArgumentNullException ex)
        //    {
        //        Assert.AreEqual("type", ex.ParamName);
        //    }
        //}

        [TestMethod]
        public void MetadataCache_Test4()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));

            var source1 = result.Properties["CalculatedSource1"];
            var target = result.Properties["CalculatedTarget"];

            Assert.IsTrue(source1.AffectsCalculatedFields);
            CollectionAssert.Contains(source1.CalculatedFields, target);
        }

        [TestMethod]
        public void MetadataCache_Test5()
        {
            try
            {
                MetadataCache.GetMetadata(typeof(BadMock));
                Assert.Fail("This should have thrown an exception because of the unmatched calculated field.");
            }
            catch (InvalidOperationException)
            {

            }
        }

        [TestMethod]
        public void MetadataCache_Test7()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            var mock = new Mock();
            var prop = result.Properties["PrivateProperty"];

            try
            {
                prop.InvokeGet(mock);
            }
            catch (InvalidOperationException)
            {

            }
        }

        [TestMethod]
        public void MetadataCache_Test8()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            var mock = new Mock();
            var prop = result.Properties["PrivateProperty"];

            try
            {
                prop.InvokeSet(mock, 5);
            }
            catch (InvalidOperationException)
            {

            }
        }

        //[TestMethod]
        //public void MetadataCache_Test9()
        //{
        //    var result = MetadataCache.GetMetadata(typeof(Mock));
        //    var mock = new Mock();
        //    var prop = result.Properties["PrivateProperty"];
        //    var targetProp = result.Properties["PublicProperty"];

        //    try
        //    {
        //        targetProp.AddCalculatedField(prop);
        //        Assert.Fail("Expected an ArgumentException");
        //    }
        //    catch (ArgumentException ex)
        //    {
        //        Assert.AreEqual("affectedProperty", ex.ParamName);
        //    }
        //}


        //[TestMethod]
        //public void MetadataCache_Test10()
        //{
        //    var result = MetadataCache.GetMetadata(typeof(Mock));
        //    var mock = new Mock();
        //    var prop = result.Properties["PrivateProperty"];

        //    try
        //    {
        //        prop.AddCalculatedField(null);
        //        Assert.Fail("Expected an ArgumentException");
        //    }
        //    catch (ArgumentNullException ex)
        //    {
        //        Assert.AreEqual("affectedProperty", ex.ParamName);
        //    }
        //}

        [TestMethod]
        public void MetadataCache_Test11()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            var mock = new Mock();
            var prop = result.Properties["PublicProperty"];
            prop.InvokeSet(mock, 5);
            var value = (int)prop.InvokeGet(mock);
            Assert.AreEqual(5, value);
        }

        [TestMethod]
        public void MetadataCache_Test12()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            try
            {
                var x = result.Properties[null];
                Assert.Fail("Expected ArgumentNullException");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
            }
        }

        [TestMethod]
        public void MetadataCache_Test13()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            try
            {
                var x = result.Properties[""];
                Assert.Fail("Expected ArgumentNullException");
            }
            catch (ArgumentException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
            }
        }

        [TestMethod]
        public void MetadataCache_Test14()
        {
            var result = MetadataCache.GetMetadata(typeof(List<int>));
            var x = result.Properties["Item [Int32]"];
            Assert.IsNotNull(x);

            var y = result.Properties["Item[]"];
            Assert.IsNotNull(y);
            Assert.AreSame(x, y);
        }

        [TestMethod]
        public void MetadataCache_Test14B()
        {
            var result = MetadataCache.GetMetadata(typeof(StringIndexedMock));
            var x = result.Properties["Item [System.String]"];
            Assert.IsNotNull(x);

            var y = result.Properties["Item[]"];
            Assert.IsNotNull(y);
            Assert.AreSame(x, y);
        }

        class StringIndexedMock
        {
            public bool this[string index]
            {
                get { return true; }
                set { }
            }
        }


        [TestMethod]
        public void MetadataCache_Test15()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            foreach (var propertyName in result.Properties.PropertyNames)
                Assert.IsTrue(result.Properties.Contains(propertyName));
        }

        [TestMethod]
        public void MetadataCache_Test16()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            foreach (var property in result.Properties)
                Assert.IsTrue(result.Properties.Contains(property));
        }

        [TestMethod]
        public void MetadataCache_Test17()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            try
            {
                var x = result.Properties.Contains((PropertyMetadata)null);
                Assert.Fail("Expected ArgumentNullException");
            }
            catch (ArgumentException ex)
            {
                Assert.AreEqual("item", ex.ParamName);
            }
        }

        [TestMethod]
        public void MetadataCache_Test18()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            try
            {
                var x = result.Properties.Contains((string)null);
                Assert.Fail("Expected ArgumentNullException");
            }
            catch (ArgumentException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
            }
        }



        [TestMethod]
        public void MetadataCache_Test20()
        {
            var result = (ICollection<PropertyMetadata>)MetadataCache.GetMetadata(typeof(Mock)).Properties;
            try
            {
                result.Add(MetadataCache.GetMetadata(typeof(String)).Properties.First());
                Assert.Fail("Expected NotImplementedException");
            }
            catch (NotImplementedException)
            {

            }
        }

        [TestMethod]
        public void MetadataCache_Test21()
        {
            var result = (ICollection<PropertyMetadata>)MetadataCache.GetMetadata(typeof(Mock)).Properties;
            try
            {
                result.Remove(MetadataCache.GetMetadata(typeof(String)).Properties.First());
                Assert.Fail("Expected NotImplementedException");
            }
            catch (NotImplementedException)
            {

            }
        }


        [TestMethod]
        public void MetadataCache_Test22()
        {
            var result = (ICollection<PropertyMetadata>)MetadataCache.GetMetadata(typeof(Mock)).Properties;
            try
            {
                result.Clear();
                Assert.Fail("Expected NotImplementedException");
            }
            catch (NotImplementedException)
            {

            }
        }

        [TestMethod]
        public void MetadataCache_Test23()
        {
            var result = (ICollection<PropertyMetadata>)MetadataCache.GetMetadata(typeof(Mock)).Properties;
            Assert.IsTrue(result.IsReadOnly);
        }

        [TestMethod]
        public void MetadataCache_Test29()
        {
            var result = (IEnumerable)MetadataCache.GetMetadata(typeof(Mock)).Properties;
            Assert.IsNotNull(result.GetEnumerator());
        }


        [TestMethod]
        public void MetadataCache_Test24()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            try
            {
                var p = result.Properties["DOES_NOT_EXIST"];
                Assert.Fail("Expected ArgumentOutOfRangeException");
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
                Assert.AreEqual("DOES_NOT_EXIST", ex.ActualValue);
            }
        }

        [TestMethod]
        public void MetadataCache_Test25()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            PropertyMetadata p;
            Assert.IsFalse(result.Properties.TryGetValue("DOES_NOT_EXIST", out p));
            Assert.IsNull(p);
        }

        [TestMethod]
        public void MetadataCache_Test26()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            PropertyMetadata p;
            Assert.IsTrue(result.Properties.TryGetValue("PublicProperty", out p));
            Assert.IsNotNull(p);
        }

        [TestMethod]
        public void MetadataCache_Test27()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            PropertyMetadata p;
            try
            {
                result.Properties.TryGetValue("", out p);
            }
            catch (ArgumentException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
            }
        }

        [TestMethod]
        public void MetadataCache_Test28()
        {
            var result = MetadataCache.GetMetadata(typeof(Mock));
            PropertyMetadata p;
            try
            {
                result.Properties.TryGetValue(null, out p);
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("propertyName", ex.ParamName);
            }
        }


        [TestMethod]
        public void MetadataCache_Test30()
        {
            var result = MetadataCache.GetMetadata(typeof(object));
            var mockProperty = MetadataCache.GetMetadata(typeof(Mock)).Properties.First();
            Assert.AreEqual(0, result.Properties.Count);
            Assert.IsFalse(result.Properties.Contains(mockProperty));
        }

        [TestMethod]
        public void MetadataCache_Test31()
        {
            var result = MetadataCache.GetMetadata(typeof(ShadowedMock));
            Assert.IsNotNull(result);
        }

    }
}
